package com.glsc.ngateway.platform.log;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.glsc.ngateway.common.api.common.enums.PlatformConstant;
import com.glsc.ngateway.common.api.ldap.dto.LdapUserDto;
import com.glsc.ngateway.common.api.ldap.dto.OaParamDto;
import com.glsc.ngateway.common.api.ldap.feign.IFeignLdapService;
import com.glsc.ngateway.common.api.platform.dto.user.PlatformUserDto;
import com.glsc.ngateway.common.api.platform.dto.user.UrlDto;
import com.glsc.ngateway.common.base.exception.PlatformException;
import com.glsc.ngateway.common.base.weblog.WebLogService;
import com.glsc.ngateway.platform.aspect.AuthHelper;
import com.glsc.ngateway.platform.aspect.CustomProperty;
import com.glsc.ngateway.platform.aspect.JwtHelper;
import com.glsc.ngateway.platform.aspect.UserCache;
import com.glsc.ngateway.platform.service.UserService;
import org.apache.logging.log4j.util.Strings;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;


@Aspect
@Component
@Order(0)
public class WebLogAspect {

    private final Logger logger = LoggerFactory.getLogger(WebLogAspect.class);

    private AuthHelper authHelper;
    private UserService userService;
    private CustomProperty customProperty;
    private UserCache userCache;


    @Resource
    private IFeignLdapService feignLdapService; // feignLdap

    @Resource
    private WebLogService webLogService;


    @Autowired
    public void setAuthHelper(AuthHelper authHelper) {
        this.authHelper = authHelper;
    }

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Autowired
    public void setCustomProperty(CustomProperty customProperty) {
        this.customProperty = customProperty;
    }

    @Autowired
    public void setUserCache(UserCache userCache) {
        this.userCache = userCache;
    }

    @Pointcut("execution(public * com.glsc.ngateway.platform.controller..*.*(..))")
    public void webLog() {
    }

    @Around("webLog()")
    public Object aroundAdvice(ProceedingJoinPoint point) throws Throwable {
        Instant start = Instant.now();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null){
            return null;
        }
        HttpServletRequest request = attributes.getRequest();
        HttpServletResponse response = attributes.getResponse();

        // log request
        Object[] args = point.getArgs();
        List<Object> list = new ArrayList<>();
        for (Object arg : args) {
            // Object[] 类型转 String 后写入日志
            if (arg instanceof Object[]) {
                list.add(Arrays.toString((Object[]) arg));
            } else if (arg instanceof Collection) {
                list.addAll((Collection<?>) arg);
            } else {
                list.add(arg);
            }
        }
        String url = request.getServletPath();
        String method = request.getMethod();
        logger.info("url: {}, ip: {}, args: {}", url, request.getRemoteAddr(), list);
//        System.out.println("params:" + params + ", interfaceName:" + interfaceName);


        String us = "";
        if (customProperty.isAuth()) {
            String token = request.getHeader("token");
            String workWechatLoginToken = request.getHeader("qywxLoginToken");
            if (!"OPTIONS".equals(method) && (Strings.isNotBlank(workWechatLoginToken) || !isWhiteListUrl(url))) {
                String username = null;
                //验证token
                if (Strings.isNotBlank(token) && !token.equals("null")) {
                    username = JwtHelper.parseToken(token);
                    //兼容oaToken
                    if (Strings.isBlank(username)) {
                        username = userService.getUserNameByLdap(token);
                    }

                    logger.info("完成解析Token,username 为:" + username + ",完成时间为：" + System.currentTimeMillis());
                    if (Strings.isNotBlank(username) && username.equals(PlatformConstant.TOKEN_OVERDUE)) {
                        if (!userService.needLoginLocal(username)) {
                            username = userService.getUserNameByLdap(token);
                            logger.info("完成用户名获取,完成时间为：" + System.currentTimeMillis());
                        }
                    }
                } else if (Strings.isNotBlank(workWechatLoginToken) && !"null".equals(workWechatLoginToken)) {
                    username = userService.getUsernameByWorkWechatLoginToken(workWechatLoginToken);
                }
                //未解析出username直接返回
                if (null == username || username.isEmpty() || username.equals(PlatformConstant.TOKEN_OVERDUE)) {
                    response.setContentType("text/html;charset=utf-8");
                    response.sendError(HttpStatus.UNAUTHORIZED.value(), (Strings.isNotBlank(token) ? "token" : "qywxLoginToken") + "不存在或者已过期");
                    return null;
                }
                us = username;
                //校验url
                //只有移动端校验url
                //TODO:设计路径校验规则，兼容PC端与移动端
                if (Strings.isNotBlank(workWechatLoginToken)) {
                    Set<String> urls = Collections.EMPTY_SET;
                    //必须配置
                    if (authHelper.requireAuth(url)) {
                        PlatformUserDto user = userCache.get(username);
                        urls = user == null ? Collections.EMPTY_SET : user.getUrls().stream().map(UrlDto::getUrl).collect(Collectors.toSet());
                        if (user == null || CollectionUtils.isEmpty(urls)) {
                            user = userService.findByUsername(username);
                            if (user == null) {
                                throw PlatformException.error("未找到用户: " + username);
                            }
                            urls = userService.queryUrlsByUserId(user.getId());
                        }
                    } else {
                        logger.info("移动登录校验失败：路径,{},必须配置校验", url);
                    }
                    if (!urls.contains(url)) {
                        response.setContentType("text/html;charset=utf-8");
                        response.sendError(HttpStatus.FORBIDDEN.value(), "无权限");
                        logger.info("移动登录校验失败：{},无权限,{}", username, url);
                        return null;
                    }
                }
            }

        }

        return webLogService.aroundAdvice(point, "platform", us);
    }

    /**
     * PC端白名单
     *
     * @param url
     * @return
     */
    private boolean isWhiteListUrl(String url) {
        return url.equals("/user/login") || url.equals("/user/logout")
                || url.startsWith("/user/findByAccount")
                || (url.startsWith("/oaflow") && !url.startsWith("/oaflow/xir"))
                || url.startsWith("/system/main/findAllBySystemIdIn")
                || url.startsWith("/dolphinScheduler")
                || url.startsWith("/swagger") || url.startsWith("/fax/listpage")
                || url.startsWith("/role/delete")
                || url.startsWith("/webjars/springfox-swagger-ui")
                || url.endsWith("/blanktemplate") || url.endsWith("Kaptcha")
                || url.endsWith("welcomefile") || url.endsWith("loginPortal")
                || url.endsWith("/jobcontrol/run")
                || url.endsWith("/user/findByUserName")
                || url.endsWith("/flowable/processDiagram")
                || url.endsWith("/api/systemConfig/findByCode")
                || url.startsWith("/common/picDownload")
                || url.equals("/api/webFieldConfig/tableFormField/findById")  //前端字段配置。内部开发环境用
                || url.equals("/api/webFieldConfig/flowFormField/findById")
                || url.endsWith("/syncAllIssueeScheduled")
                || url.endsWith("/syncGiteeIssuee")
                || url.contains("/api/smartbi/menuManager")
                || url.endsWith("/api/res/system/file/getContentById")
                ;
    }


    /**
     * @param usrJson
     * @return
     * @Description 根据JSON 解析UserName
     */
    public String getUserName(String usrJson) {

        JSONObject jsonObject = JSON.parseObject(usrJson);
        String codeNumber = jsonObject.getString(PlatformConstant.RETURN_CODE_KEY);

        if (codeNumber.equals(PlatformConstant.SUCCESS)) {
            JSONObject objectData = jsonObject.getJSONObject(PlatformConstant.RETURN_DATA_KEY);
            String userName = objectData.getString(PlatformConstant.RETURN_USERNAME_KEY);
            return userName;
        } else {
            return PlatformConstant.TOKEN_OVERDUE;
        }
    }
}
